#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/io.h>
#include <unistd.h>
#include "parport.h"
#include "ppacmd.h"

/*
 * Non standard mojo to escape from pass-through mode
 */
void LogiEscapeSequenceLong(uint32_t base)
{
    uint8_t ctrl;
    
    ctrl = read_control_port(base);
    write_control_port(base, (ctrl & 0xF0) | 0xC);
    ctrl = read_control_port(base);
    write_control_port(base, ctrl & 0x5F);
    
    outb(0x2B, base);
    outb(0xAB, base);
    outb(0xD4, base);
    outb(0x54, base);
    outb(0x2B, base);
    outb(0xAB, base);
    outb(0xD4, base);
    outb(0x54, base);
}

int LogiNegociation(uint32_t base, uint8_t negociation_byte)
{
    uint8_t ctrl;
    LogiEscapeSequenceLong(base);
  
    write_control_port(base, 6);
 // write_negociation_byte(base, 0x40);
    ctrl = read_control_port(base);
    write_control_port(base, ctrl & 0x5F);

    outb(negociation_byte, base);
  
    if ( wait_status1(base) )
    {
        printf("LogiNegociation wait_status1 ok\n");

        write_control_port(base, 7);
        write_control_port(base, 4);
        return wait_status_ack(base);
    }
    return 0;
}


uint32_t ReadRegister(uint32_t base, uint32_t register_num, uint8_t* out)
{
    if ( register_num && register_num < 64)
    {
        write_register_epp(base, 1, register_num);
  
        read_register_epp(base, 2, out);
    }
    else
    {
        read_register_epp(base, register_num, out);
    }
    return 0;
}

uint32_t WriteRegister(uint32_t base, uint32_t register_num, uint8_t data)
{
    if (register_num < 48)
    {
        write_register_epp(base, 1, register_num);
        return write_register_epp(base, 2, data);
    }
    else
    {
        return write_register_epp(base, register_num, data);
    }
}

void DumpRegisters(uint32_t base)
{
    int i;
    uint8_t reg;

    printf("---Dumping registers---\n");
    
    for(i=0 ; i < 100 ; i++)
    {
        ReadRegister(base, i, &reg);
        printf("R%d \t %x\n", i, reg);
    }

    printf("-----------------------\n");
}

void SwitchLight(uint32_t base, bool on)
{
    uint8_t r3 = 0;

    ReadRegister(base, 0x3, &r3);
    
    if (on)
        r3 = (r3 & 0xBF) | 0x20;
    else
        r3 = (r3 & 0xDF) | 0x40;
    
    WriteRegister(base, 0x3, r3);
}

void ResetHardware(uint32_t base)
{
    WriteRegister(base, 68, 0x80);
    WriteRegister(base, 72, 0x88);
    WriteRegister(base, 72, 0);
    WriteRegister(base, 67, 0x80);
    WriteRegister(base, 67, 0);
    WriteRegister(base, 58, 58);
        
}


/**
motor speed : reg 24
mask 0x40 et 0x20   => motor direction  bit
speed 5 bits : 11111 =  31 = stop
**/
void SetMotorSpeed(uint32_t base, uint32_t speed, bool forward)
{
    uint8_t reg24;

    ReadRegister(base, 24, &reg24);

    uint8_t v6 = (uint8_t) forward;
    reg24 ^= (reg24 ^ (v6 << 6)) & 0x40;
    reg24 ^= (reg24 ^ (32 * v6)) & 0x20;
    reg24 ^= (speed ^ reg24) & 0x1F;
    
    WriteRegister(base, 24, reg24);
}

/**
registre 23 (0x17)
not in scan mode
    mask 0x10   => scan button
    mask 0x01   => paper sensor inverted

scan mode
**/
void GetSwitchesState(uint32_t base)
{
    uint8_t reg75,reg72,reg23;

    ReadRegister(base, 75, &reg75);
    ReadRegister(base, 72, &reg72);
    
    ReadRegister(base, 23, &reg23);
}

void SetDummyCountOnPPA(uint32_t base, uint32_t count)
{
    WriteRegister(base, 66, count & 0xFF );
    WriteRegister(base, 67, count >> 8);
}

uint32_t GetDummyCountOnPPA(uint32_t base)
{
    uint8_t low, high;
    
    ReadRegister(base, 66, &low);
    ReadRegister(base, 67, &high);
    
    return low + (high << 8);
}


void SetTargetLineCount(uint32_t base, uint32_t count)
{
    uint8_t reg68;
    ReadRegister(base, 68, &reg68);
    
    reg68 ^= (count ^ reg68) & 0x3F;
    
    WriteRegister(base, 68, 0x85);

}


void SetScanWidthOnPPA(uint32_t base, uint32_t w)
{
    uint32_t a;
   
    WriteRegister(base, 69, 0x0B); 
    SetTargetLineCount(base, 108);

    a = GetDummyCountOnPPA(base) + ( w * 2);
    //WriteRegister(64, a);
    //WriteRegister(65, a >> 8);
    WriteRegister(base, 64, 0xEE);
    WriteRegister(base, 65, 0x13);
}

void InitMotorSpeed(uint32_t base)
{
    uint8_t max, min, start;
    max = 13;
    min = 13;
    start = 13;

    if ( min == 0 )
    {
        min = max / 2;
        min = (min != 0) ? min : 1;
    }
    
    /*WriteRegister(70, start);
    
    WriteRegister(71, (max << 3) ^ start);
    
    WriteRegister(70, start ^ min);
*/
    WriteRegister(base, 0x46, 0x40);
    WriteRegister(base, 0x47, 0xB9);
    WriteRegister(base, 0x46, 0xD2);
}

uint32_t GetFIFOlineCount(uint32_t base)
{
    uint8_t reg77,reg76;
    
    ReadRegister(base, 76, &reg76);
    ReadRegister(base, 77, &reg77);

    write_epp_addr(base, 73);

    return reg77 + 2 * ((reg76 & 0x70) >> 4);
}

void SwitchToScanMode(uint32_t base)
{
    uint8_t reg3=0;

    SwitchLight(base, 1);
    WriteRegister(base, 79, 0);
    WriteRegister(base, 77, 0);
    
    InitMotorSpeed(base);
    ReadRegister(base, 3, &reg3);
    WriteRegister(base, 3, reg3 | 0x80); 
    
    ReadRegister(base,2, &reg3);
    WriteRegister(base, 2, reg3 | 0x80); 
    
    WriteRegister(base, 72, 0xD4);

    int i;
    for (i =0; i < 10 ; i++)
    {
        printf("GetFIFOlineCount = %d\n", GetFIFOlineCount(base));
        
        sleep(3);
    }
}

void SetHwInScanMode(uint32_t base)
{
    uint32_t x = 7;
    uint32_t y = 151;
    
    SetDummyCountOnPPA(base, 2 * x);
   
    SetScanWidthOnPPA(base, y);
    
    /* Skip for freescan
    SetResolutionOnPPA(base, r);
    
    SetBitsPerPixelOnPPA(base, bpp);
    
    SetControlScanOnPPA(base, ctrl);
    
    SetThresholdOnPPA(base, thr);
    
    SetBrightnessOnPPA(base, bgt);
    
    SetGraduationOnPPA(base, gdt);
    */

    SwitchToScanMode(base);
}


void ReadData(uint32_t base)
{
    uint8_t buffer[256]={0};
    uint8_t reg;
    
    while(1)
    {
        printf("GetFIFOlineCount = %d\n", GetFIFOlineCount(base));
        
        write_epp_addr(base, 73);
        
        insb(base+4, buffer, 256);
        
        for(reg=0; reg < 10 ; reg++)
        {
            printf("%02x ", buffer[reg]);
        }
    }

}

bool IsPowerPlugged(uint32_t base)
{
    uint8_t reg78;

    ReadRegister(base, 78, &reg78);

    printf("IsPowerPlugged = %d\n", reg78 & 0x80);
    return reg78 & 0x80;
}

bool TestCommunication(uint32_t base)
{
    uint8_t test_read[256] = {0};
    uint32_t i,j;
    bool ok = true;
    
    WriteRegister(base, 79, 0);
    WriteRegister(base, 70, 0x20);
  
    write_epp_addr(base, 73);

    for(i=0 ; i < 10 ; i++)
    {
        insb(base+4, test_read, 256);
        
        for ( j = 0; j < 256; j++ )
        {
            if ( test_read[j] != j)
            {
                ok = false;
                break;
            }
        }
    }
    printf("TestCommunication = %d\n", ok);
    return ok;
}

void ReadPPAID(uint32_t base)
{
    static char* ppa_names[] = {
        "noPPA",
        "PPA_2",
        "PPA_3",
        "PPA_4",
        "lastPPA"
    };
    uint8_t reg, ppa_id=0;
    
    ReadRegister(base, 78, &reg);

    reg &= 0xF;

    if (reg == 2)
    {
        ppa_id = 1;
    }
    else if (reg == 3)
    {
        ReadRegister(base, 74, &reg); 

        if (reg == 0x7A)
            ppa_id = 3;
        else
            ppa_id = 2; 
    }

    printf("PPA ID = %d => %s\n", ppa_id, ppa_names[ppa_id]);   
}

void ReadScannerID(uint32_t base)
{
    /* 1 => Pagescan ... 4=> Freescan */
    static char* scanner_names[] = {
        "PageScan",
        "Smc2000",
        "PageScanPro",
        "FreeScan",
        "PageScanUSB"
    };
    uint8_t id;

    ReadRegister(base, 0, &id);  
    
    id &= 0x1F;

    //switch reg : 3=>1 4=>3 5=>4 13=>2
    switch(id)
    {
        case 3:
            id = 1;
            break;
        case 4:
            id = 3;
            break;
        case 5:
            id = 4;
            break;
        case 13:
            id = 2;
            break;
    }
    if (id <= 5)
    {
        printf("ReadScannerID = %d => %s\n", id, scanner_names[id-1]);
    }   
}

